home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MACD 5
/
MACD 5.bin
/
workbench
/
datatypes
/
marktextdtc014.lha
/
markabletextdtclass
/
markabletextdtclass.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-03-12
|
26KB
|
852 lines
/*
**
** $VER: markabletextdtclass.c 1.4 (12.3.97)
** markabletextdtclass 1.4
**
** main file
**
** (C) Copyright 1996/1997 by Roland 'Gizzy' Mainz
** All Rights Reserved
**
*/
/* amiga includes */
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/alerts.h>
#include <dos/dostags.h>
#include <intuition/icclass.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/textclass.h>
/* amiga prototypes */
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/datatypes_protos.h>
#include <clib/dtclass_protos.h>
#include <clib/alib_protos.h>
/* amiga pragmas */
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/datatypes_pragmas.h>
#include <pragmas/dtclass_pragmas.h>
#include <pragmas/alib_pragmas.h> /* tagcall stubs */
/* ANSI includes */
#include <string.h>
/* version string */
#include "markabletextdtclass_rev.h"
/* misc defines */
#ifndef NAME
#define NAME "markabletextdtclass"
#endif /* !NAME */
/* SASC specific defines */
#define DISPATCHERFLAGS __saveds __asm
#define REGARGS __asm
#define REGA0 register __a0
#define REGA1 register __a1
#define REGA2 register __a2
/* version_string */
STRPTR versionstring = VERSTAG;
#define V( x ) ((VOID *)(x))
#define G( o ) ((struct Gadget *)(o))
#define EXTG( o ) ((struct ExtGadget *)(o))
#define XTAG( expr, tagid ) ((Tag)((expr)?(tagid):(TAG_IGNORE)))
long main_retval,
main_retval2;
extern struct ExecBase *SysBase;
extern struct Library *DOSBase;
struct ClassBase
{
struct ExecBase *cb_SysBase;
struct Library *cb_IntuitionBase;
struct Library *cb_DataTypesBase;
};
/* markabletextdtclass instance data */
struct MarkableTextDTClassInstData
{
ULONG *mtcid_Methods; /* If NULL => patch disabled (possibly in directory.datatype,
* which includes the same "mark" workaround).
*/
BOOL mtcid_InMarkMode;
};
const
ULONG markabletextdtclass_includemethods[] =
{
DTM_SELECT,
DTM_CLEARSELECTED,
GM_GOACTIVE,
GM_HANDLEINPUT,
GM_GOINACTIVE,
GM_RENDER,
OM_NEW,
OM_DISPOSE,
OM_UPDATE,
OM_SET,
(~0UL)
};
const
ULONG markabletextdtclass_excludemethods[] =
{
(~0UL)
};
REGARGS struct IClass *(*OldObtainEngine)( void );
/* prototypes */
static REGARGS struct IClass *NewObtainEngine( void );
static DISPATCHERFLAGS ULONG Dispatch( REGA0 struct IClass *, REGA2 Object *o, REGA1 Msg );
static ULONG *CopyDTSupportedMethods( struct ClassBase *, Object *, ULONG *, ULONG * );
static ULONG NumMethods( ULONG * );
static ULONG *FindMethod( ULONG *, ULONG );
static ULONG notifyAttrChanges( Object *, struct GadgetInfo *, ULONG, Tag, ... );
static BOOL AttemptRemoveFunction( struct ClassBase *, struct Library *, LONG, APTR, APTR );
struct IClass *MarkableTextDTClass = NULL;
struct ClassBase *cb = NULL; /* global context */
/****** markabletextdtclass/markabletextdtclass ******************************
*
* NAME
* markabletextdtclass -- patches text.datatype to support marking
*
* FORMAT
* markabletextdtclass
*
* TEMPLATE
* ,
*
* PATH
* markabletextdtclass:
*
* PURPOSE
* This patch allows marking in all text.datatype (ascii, IFF FTXT
* C/C++-Source etc.)-based objects.
*
* DESCRIPTION
* During creation of mpeg#?.datatype (misc datatypes for system/video/
* audiostreams) and SwitchWindow (object-oriented BOOPSI environment
* (analyse, create or modify BOOPSI objects including datatypes ones)),
* I wrote a directory.datatype. Up to version 1.3 I had problems with
* DTM_COPY/DTM_WRITE. As a part of the fix work I found the BUG
* causing marking (DTM_SELECT) don't work properly. Later I extracted
* the code for this and put it into this project.
*
* KNWON BUGS
* - HACK
*
* - not well analysed (possible memory loss etc. (not seen, but
* possible))
*
* HISTORY
* V1.1
* - First Aminet release
*
* V1.2
* - Disables the patch when running in a directory.datatype object
* (directory.datatype contains the same mark patch, doing the same
* things twice is not very usefull).
*
* - Avoids the usage of this patch when a text.datatype > V40 occurs
* in the system.
*
* V1.3
* - Fixed a longstandig bug:
* Because the text.datatype/ObtainEngine-function wasn't patched,
* plane text.datatype-Objects didn't include the patch.
*
* Another effect of this was that other text.datatype-patches can't
* start (like Stefan Rupperts textdtpatch
* (Aminet:utl/dtype/TEXTDTPTACH39_2.LhA))
* (Reported by Timo C. Nentwig (tcn@oxygen.in-berlin.de); Thanks!).
*
* V1.4
* - Recompiled with SAS/C 6.57
*
* - Moved contents of the #?.guide file into this autodoc.
*
* - The V1.4 release fixes a longstanding bug: The cl_UserData field
* is used by datatypes.library to store the class library base.
* Now the cl_UserData is NULL (avoids problems), and the compiler
* context is accessed in another way.
*
* - Added a small patch which fixes the buggy scrolling routine
* when drag-moving (e.g. click on a word, then move mouse
* out if the text object; the text will scroll in the specified
* direction) the text.
*
* NOTE
*
* INSTALLATION
* After unpacking this archieve:
*
* Shell:
* - Unpack this archieve and copy the markabletextdtclass to
* SYS:Utilities/
* Copy CLONE FROM markabletextdtclass TO SYS:Utilities/
* Put "run <>NIL: markabletextdtclass:markabletextdtclass" in your
* "S:user-startup".
*
* make/smake (SAS):
* - Type 'smake install' in shell
*
* USAGE
* Run the programm, it will attect all new object (and subclasses of
* text.datatype (if they don't redefine the DTA_Methods attribute like
* directory.datatype does).
* The programm can be finished with a CTRL_C signal
* (or Break <CLI_ID> C). It will wait for outstanding object's.
*
* AUTHOR's REQUEST
* By releasing this program I do not place any obligations on you,
* feel free to share this program with your friends (and enemies).
*
* If you want to blame me, report any bugs, or wants a new version
* send your letter to:
* Roland Mainz
* Hohenstaufenstraße 8
* 52388 Nörvenich
* GERMANY
*
* Phone: (+49)(0)2426/901568
*
* EMAIL is also available:
* GISBURN@w-specht.rhein-ruhr.de
*
* If you want to send me attachments larger than 1MB (up to 5MB,
* more with my permission):
* Up to March 1997 I'm reachable using this email address, too:
* Reinhold.A.Mainz@KBV.DE
*
* | Please put your name and address in your mails !
* | German mailers should add their phone numbers.
* | See BUGS section above when submitting bug reports.
*
* Sorry, but I can only look once a week for mails.
* If you don't hear something from me within three weeks, please
* send your mail again (but watch about new releases) (problems with
* this email port are caused by reconfigurations, hackers, network
* problems etc.).
*
* The entire "markabletextdtclass" package may be noncommercially
* redistributed, provided that the package is always distributed
* in it's complete form (including it's documentation). A small copy
* fee for media costs is okay but any kind of commercial distribution
* is strictly forbidden! Comments and suggestions how to improve
* this program are generally appreciated!
*
* Thanks to David Junod, who wrote the animation.datatype and the
* datatypes example code, Matt Dillon for his DICE, and Olaf 'Olsen'
* Barthel for his help, ideas and some text clips from his
* documentations.
*
* SEE ALSO
* text.datatype, directory.datatype,
* exec.library/SetFunction
*
*****************************************************************************
*
*/
int main( void )
{
main_retval2 = 0L;
main_retval = RETURN_OK;
/* Be sure we've a post-V38-System (DataTypes is only available on post-V38-Systems) */
if( (SysBase -> LibNode . lib_Version) >= 39UL )
{
if( cb = (struct ClassBase *)AllocMem( (ULONG)sizeof( struct ClassBase ), (MEMF_PUBLIC | MEMF_CLEAR) ) )
{
cb -> cb_SysBase = SysBase;
#define SysBase (cb -> cb_SysBase)
if( cb -> cb_IntuitionBase = OpenLibrary( "intuition.library", 39UL ) )
{
#define IntuitionBase (cb -> cb_IntuitionBase)
if( cb -> cb_DataTypesBase = OpenLibrary( "datatypes.library", 39UL ) )
{
#define DataTypesBase (cb -> cb_DataTypesBase)
struct Library *DTClassBase;
if( DTClassBase = OpenLibrary( "datatypes/text.datatype", 39UL ) )
{
/* Avoid usage of this tool when using text.datatype > V40 */
if( (DTClassBase -> lib_Version) <= 40U )
{
struct IClass *TextDTClass;
/* Obtain text.datatype class ptr */
if( TextDTClass = ObtainEngine() )
{
ClassID oldclassid;
oldclassid = TextDTClass -> cl_ID; /* Save text.datatype's public class name */
TextDTClass -> cl_ID = "non_markable_text.datatype"; /* HACK: Replace text.datatype's public class name */
/* Create our new class... */
if( MarkableTextDTClass = MakeClass( TEXTDTCLASS, NULL, TextDTClass, (ULONG)sizeof( struct MarkableTextDTClassInstData ), 0UL ) )
{
BOOL classvalid = TRUE, /* Our markable_text.datatype valid ? */
ispatched = TRUE; /* ObtainEngine patch installed ? */
/* Prepare class for usage... */
MarkableTextDTClass -> cl_Dispatcher . h_Entry = (HOOKFUNC)Dispatch;
MarkableTextDTClass -> cl_UserData = 0UL; /* datatypes.library expects here a (struct Library *) or NULL,
* we cannot use this for our context
*/
/* Patch text.datatype/ObrainEngine to return our class */
OldObtainEngine = (REGARGS struct IClass *(*)( void ))SetFunction( DTClassBase, (-30L), (ULONG (*)())NewObtainEngine );
/* Make our class public */
AddClass( MarkableTextDTClass );
/* Wait (until user want to quit) */
Wait( SIGBREAKF_CTRL_C );
/* Remove class from public */
RemoveClass( MarkableTextDTClass );
/* Loop until both patched function and our class are freed */
do
{
if( AttemptRemoveFunction( cb, DTClassBase, (-30L), (APTR)OldObtainEngine, (APTR)NewObtainEngine ) )
{
/* Wait for possible users of the patched function */
Delay( (TICKS_PER_SECOND / 2UL) );
ispatched = FALSE;
}
/* The class can only be freed AFTER our patch has been removed... */
if( classvalid && (ispatched == FALSE) )
{
if( FreeClass( MarkableTextDTClass ) )
{
MarkableTextDTClass = NULL;
classvalid = FALSE;
}
}
} while( classvalid || ispatched );
/* Let other tasks partake (avoids possible race-conditions) */
Delay( (TICKS_PER_SECOND / 2UL) );
/* Done ! */
}
else
{
Printf( "can't make new text.datatype class ! (no memory or an other incompatible patch)\n" );
main_retval2 = ERROR_NO_FREE_STORE;
main_retval = RETURN_FAIL;
}
TextDTClass -> cl_ID = oldclassid;
}
else
{
Printf( "can't obtain text.datatype\n" );
main_retval2 = ERROR_NO_FREE_STORE;
main_retval = RETURN_FAIL;
}
}
else
{
Printf( "text.datatype > V40 not supported\n" );
main_retval2 = ERROR_OBJECT_WRONG_TYPE;
main_retval = RETURN_FAIL;
}
CloseLibrary( DTClassBase );
}
else
{
Printf( "can't open text.datatype\n" );
main_retval2 = ERROR_NO_FREE_STORE;
main_retval = RETURN_FAIL;
}
CloseLibrary( (cb -> cb_DataTypesBase) );
}
else
{
Printf( "can't open datatypes.library\n" );
main_retval2 = ERROR_NO_FREE_STORE;
main_retval = RETURN_FAIL;
}
CloseLibrary( (cb -> cb_IntuitionBase) );
}
else
{
Printf( "can't open intuition.library\n" );
main_retval2 = ERROR_NO_FREE_STORE;
main_retval = RETURN_FAIL;
}
FreeMem( (APTR)cb, (ULONG)sizeof( struct ClassBase ) );
}
else
{
main_retval2 = ERROR_NO_FREE_STORE;
main_retval = RETURN_FAIL;
}
}
else
{
main_retval2 = 0L;
main_retval = RETURN_FAIL;
}
PrintFault( main_retval2, NAME );
SetIoErr( main_retval2 );
return( main_retval );
}
/* text.datatype/ObtainEngine patch */
static
DISPATCHERFLAGS
struct IClass *NewObtainEngine( void )
{
return( MarkableTextDTClass ); /* Return our new class... */
}
/* MarkableTextDTClass dispatcher */
static
DISPATCHERFLAGS
ULONG Dispatch( REGA0 struct IClass *cl, REGA2 Object *o, REGA1 Msg msg )
{
struct MarkableTextDTClassInstData *mtcid;
ULONG retval = 0UL;
switch( msg -> MethodID )
{
case OM_NEW:
{
if( retval = DoSuperMethodA( cl, o, msg ) )
{
ClassID classid = (OCLASS( (Object *)retval ) -> cl_ID);
/* Get a pointer to the object data */
mtcid = (struct MarkableTextDTClassInstData *)INST_DATA( cl, retval );
/* Check if the we're running inside a "directory.datatype" object
* TRUE => disable patch => mtcid -> mtcid_Methods = NULL;
* FALSE => set up methods table
*/
if( (classid)?(strcmp( classid, "directory.datatype" )):(1) )
{
/* Create array of supported methods (see OM_GET) */
if( !(mtcid -> mtcid_Methods = CopyDTSupportedMethods( cb, (Object *)retval, (ULONG *)markabletextdtclass_includemethods, (ULONG *)markabletextdtclass_excludemethods )) )
{
/* Can't build DTA_Methods list */
CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
retval = 0UL;
SetIoErr( ERROR_NO_FREE_STORE );
}
}
}
}
break;
case OM_DISPOSE:
{
mtcid = (struct MarkableTextDTClassInstData *)INST_DATA( cl, o );
/* Free methods array */
FreeVec( (mtcid -> mtcid_Methods) );
DoSuperMethodA( cl, o, msg );
}
break;
case OM_GET:
{
mtcid = (struct MarkableTextDTClassInstData *)INST_DATA( cl, o );
/* Redefine DTA_Methods to reflect all supported methods */
if( ((((struct opGet *)msg) -> opg_AttrID) == DTA_Methods) && (mtcid -> mtcid_Methods) )
{
*(((struct opGet *)msg) -> opg_Storage) = (ULONG)(mtcid -> mtcid_Methods);
retval = 1UL;
}
else
{
retval = DoSuperMethodA( cl, o, msg );
}
}
break;
case OM_UPDATE:
{
/* Avoid OM_NOTIFY loops */
if( DoMethod( o, ICM_CHECKLOOP ) )
{
break;
}
}
case OM_SET:
{
/* Pass the attributes to the text class and force a refresh
* if we need it
*/
if( retval = DoSuperMethodA( cl, o, msg ) )
{
/* Top instance ? */
if( OCLASS( o ) == cl )
{
struct RastPort *rp;
/* Get a pointer to the rastport */
if( rp = ObtainGIRPort( (((struct opSet *)msg) -> ops_GInfo) ) )
{
struct gpRender gpr;
/* Force a redraw */
gpr . MethodID = GM_RENDER;
gpr . gpr_GInfo = ((struct opSet *)msg) -> ops_GInfo;
gpr . gpr_RPort = rp;
gpr . gpr_Redraw = GREDRAW_UPDATE;
DoMethodA( o, (Msg)(&gpr) );
/* Release the temporary rastport */
ReleaseGIRPort( rp );
}
retval = 0UL;
}
}
}
break;
case GM_GOACTIVE:
case GM_HANDLEINPUT:
{
struct DTSpecialInfo *si = (struct DTSpecialInfo *)(G( o ) -> SpecialInfo);
struct gpInput *gpi = (struct gpInput *)msg;
mtcid = (struct MarkableTextDTClassInstData *)INST_DATA( cl, o );
/* Patch enabled ? */
if( mtcid -> mtcid_Methods )
{
/* Check if the user starts marking */
if( (mtcid -> mtcid_InMarkMode) == FALSE )
{
if( (si -> si_Flags) & DTSIF_DRAGSELECT )
{
mtcid -> mtcid_InMarkMode = TRUE;
/* Clear previous marked area */
DoMethod( o, DTM_CLEARSELECTED, (gpi -> gpi_GInfo) );
}
}
}
retval = DoSuperMethodA( cl, o, msg );
}
break;
case GM_GOINACTIVE:
{
struct gpGoInactive *gpgi = (struct gpGoInactive *)msg;
mtcid = (struct MarkableTextDTClassInstData *)INST_DATA( cl, o );
retval = DoSuperMethodA( cl, o, msg );
/* Patch enabled ? */
if( mtcid -> mtcid_Methods )
{
/* If we were in mark mode... */
if( mtcid -> mtcid_InMarkMode )
{
struct gpLayout gpl;
mtcid -> mtcid_InMarkMode = FALSE;
/* Do size layout (and send DTA_Sync to force a refresh) */
gpl . MethodID = GM_LAYOUT;
gpl . gpl_GInfo = gpgi -> gpgi_GInfo;
gpl . gpl_Initial = 0L;
DoMethodA( o, (Msg)(&gpl) );
notifyAttrChanges( o, (gpgi -> gpgi_GInfo), 0UL,
GA_ID, (ULONG)(G( o ) -> GadgetID),
DTA_Sync, 1UL,
TAG_DONE );
}
}
}
break;
case GM_RENDER:
{
/* Drag-moving patch; scolls ALL bitplanes when moving */
if( (G( o ) -> Activation) & GACT_ACTIVEGADGET )
{
/* Copy msg */
struct gpRender gpr = *((struct gpRender *)msg);
/* Force a redraw */
gpr . gpr_Redraw = GREDRAW_REDRAW;
retval = DoSuperMethodA( cl, o, (Msg)(&gpr) );
}
else
{
retval = DoSuperMethodA( cl, o, msg );
}
}
break;
case DTM_CLEARSELECTED:
{
mtcid = (struct MarkableTextDTClassInstData *)INST_DATA( cl, o );
/* Patch enabled ? */
if( mtcid -> mtcid_Methods )
{
struct IBox *selectdomain;
struct RastPort *rp;
/* Put my "magic" "nothing selected" value into DTA_SelectDomain and
* remove the DTSIF_HIGHLIGHT flag
*/
if( GetAttr( DTA_SelectDomain, o, (ULONG *)(&selectdomain) ) == 1UL )
{
if( selectdomain )
{
static const struct IBox all_selected = { ~0, ~0, ~0, ~0 };
SetAttrs( o, DTA_SelectDomain, (&all_selected), TAG_DONE );
}
((struct DTSpecialInfo *)(G( o ) -> SpecialInfo)) -> si_Flags &= ~DTSIF_HIGHLIGHT;
}
/* Get a pointer to the rastport */
if( rp = ObtainGIRPort( (((struct dtGeneral *)msg) -> dtg_GInfo) ) )
{
struct gpRender gpr;
/* Force a redraw */
gpr . MethodID = GM_RENDER;
gpr . gpr_GInfo = ((struct dtGeneral *)msg) -> dtg_GInfo;
gpr . gpr_RPort = rp;
gpr . gpr_Redraw = GREDRAW_REDRAW;
DoMethodA( o, (Msg)(&gpr) );
/* Release the temporary rastport */
ReleaseGIRPort( rp );
}
}
/* The following statement would only be usefull if we're running on a post-V40 text.datatype
* Because the startup-code of this patch excludes this, I've disabled the statement below
*/
#ifdef COMMENTED_OUT
else
{
retval = DoSuperMethodA( cl, o, msg );
}
#endif /* COMMENTED_OUT */
}
break;
/* Let the superclass handle everything else */
default:
{
retval = DoSuperMethodA( cl, o, msg );
}
break;
}
return( retval );
}
/* Create a list of supported methods of this class, including all methods we support (DTA_Methods) */
static
ULONG *CopyDTSupportedMethods( struct ClassBase *cb, Object *o, ULONG *includemethods, ULONG *excludemethods )
{
if( o )
{
ULONG *methods;
if( methods = GetDTMethods( o ) )
{
ULONG numincludemethods,
nummethods;
ULONG *copymethods;
nummethods = NumMethods( methods );
numincludemethods = NumMethods( includemethods );
if( copymethods = (ULONG *)AllocVec( ((nummethods + numincludemethods + 2UL) * sizeof( ULONG )), (MEMF_PUBLIC | MEMF_CLEAR) ) )
{
ULONG *x;
/* Copy methods, including their terminator */
memcpy( (void *)copymethods, (void *)methods, (size_t)((nummethods + 1UL) * sizeof( ULONG )) );
if( includemethods )
{
/* Find terminator (== ~0UL) of copymethods */
if( x = FindMethod( copymethods, (~0UL) ) )
{
memcpy( (void *)x, (void *)includemethods, (size_t)((numincludemethods + 1UL) * sizeof( ULONG )) );
}
}
if( excludemethods )
{
while( (*excludemethods) != (~0UL) )
{
if( x = FindMethod( copymethods, (*excludemethods) ) )
{
*x = OM_NEW;
}
excludemethods++;
}
}
return( copymethods );
}
}
}
return( NULL );
}
static
ULONG NumMethods( ULONG *methods )
{
ULONG num = 0UL;
if( methods )
{
while( (*methods) != (~0UL) )
{
methods++;
num++;
}
}
return( num );
}
static
ULONG *FindMethod( ULONG *methods, ULONG MethodID )
{
if( methods )
{
while( ((*methods) != (~0UL)) && ((*methods) != MethodID) )
{
methods++;
}
if( (*methods) == MethodID )
{
return( methods );
}
}
return( NULL );
}
static
ULONG notifyAttrChanges( Object *o, struct GadgetInfo *ginfo, ULONG flags, Tag tag1, ... )
{
struct opUpdate opu;
opu . MethodID = OM_NOTIFY;
opu . opu_AttrList = (struct TagItem *)(&tag1);
opu . opu_GInfo = ginfo;
opu . opu_Flags = flags;
return( DoMethodA( o, (Msg)(&opu) ) );
}
static
BOOL AttemptRemoveFunction( struct ClassBase *cb, struct Library *lib, LONG offset, APTR oldfunc, APTR patchfunc )
{
BOOL removed = FALSE;
if( lib && offset && oldfunc && patchfunc )
{
APTR currfunc;
Forbid();
currfunc = (APTR)SetFunction( lib, offset, (ULONG (*)())oldfunc );
/* Does the code returned from SetFunction match our patch code ? */
if( currfunc == patchfunc )
{
removed = TRUE;
}
else
{
/* No, restore returned code */
currfunc = (APTR)SetFunction( lib, offset, (ULONG (*)())currfunc );
/* The following expression MUST never be TRUE */
if( currfunc != oldfunc )
{
/* havoc found, crash expected */
Alert( AN_Unknown + 0x1357 );
}
}
Permit();
}
return( removed );
}